package com.vrp3d.common.utils;

import com.vrp3d.common.enums.CommonEnum;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * 日期工具类
 *
 * @author vrp3d
 */
public class DateUtil {
    private static final String FORMATTER = "yyyy-MM-dd HH:mm:ss";
    private static final String HORIZONTAL_LINE_FORMATTER = "yyyyMMddHHmmss";
    private static final String SIMPLE_FORMATTER = "yyyy-MM-dd";
    private static final String MONTH_FORMATTER = "yyyy-MM";
    private static final String YYYY_MM_DD = "yyyyMMdd";

    private final static String[] REPLACE_STRING = new String[]{"GMT+0800", "GMT+08:00"};
    private final static String SPLIT_STRING = "(中国标准时间)";

    /**
     * Hours per day
     * Minutes per hour
     * Minutes per day
     * Seconds per minute
     * Seconds per hour
     * Seconds per day
     * days in a month
     */
    public static final int HOURS_PER_DAY = 24;
    public static final int MINUTES_PER_HOUR = 60;
    public static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
    public static final int SECONDS_PER_MINUTE = 60;
    public static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
    public static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
    public static final int DAYS_IN_A_MONTH = 30;

    /**
     * 获取文件存储路径
     *
     * @return 格式是year/month/day
     */
    public static String getFileStoragePath() {
        String separatorSlash = CommonEnum.SEPARATOR_SLASH.getMsg();
        // year
        String s = separatorSlash + getNowYear() + separatorSlash;
        // month
        Integer nowMonth = getNowMonth();
        if (nowMonth < 10) {
            s += "0";
        }
        s += nowMonth;
        s += separatorSlash;
        // day
        Integer nowDay = getNowDay();
        if (nowDay < 10) {
            s += "0";
        }
        s += nowDay;
        s += separatorSlash;
        // hour
        Integer nowHour = getNowHour();
        if (nowHour < 10) {
            s += "0";
        }
        s += nowHour;
        s += separatorSlash;
        //
        Integer nowMinute = getNowMinute();
        if (nowMinute < 10) {
            s += "0";
        }
        s += nowMinute;
//        s += separatorSlash;
        return s;
    }

    /**
     * oss获取失效时间
     *
     * @return
     */
    public static Date getExpirationTime() {
        return new Date(System.currentTimeMillis() + DateUtil.howManyMillisecondUntilMidnight());
//        return new Date(System.currentTimeMillis() + CommonEnum.OSS_ACCESS_INVALID_TIME.getMsgInteger());
    }

    /**
     * GMT+0800 (中国标准时间) 00:00:00 时间格式转换
     *
     * @param dateString 时间字符串
     * @return Date
     */
    public static Date str2Date(String dateString) {
        try {
            dateString = dateString.split(Pattern.quote(SPLIT_STRING))[0].replace(REPLACE_STRING[0], REPLACE_STRING[1]);
            SimpleDateFormat sf1 = new SimpleDateFormat("E MMM dd yyyy HH:mm:ss z", Locale.US);
            return sf1.parse(dateString);
        } catch (Exception e) {
            throw new RuntimeException("时间转化格式错误" + "[dateString=" + dateString + "]" + "[FORMAT_STRING=" + FORMATTER + "]");
        }
    }

    /**
     * 到当天0点还剩多少秒
     *
     * @return Long
     */
    public static Long howManySecondsUntilMidnight() {
        return TimeUnit.NANOSECONDS.toSeconds(Duration.between(LocalDateTime.now(), getMidnightDateTime()).toNanos());
    }

    /**
     * 到当天0点还剩多少毫秒
     *
     * @return Long
     */
    public static Long howManyMillisecondUntilMidnight() {
        return TimeUnit.NANOSECONDS.toMillis(Duration.between(LocalDateTime.now(), getMidnightDateTime()).toNanos());
    }

    private static LocalDateTime getMidnightDateTime() {
        LocalTime midnight = LocalTime.MIDNIGHT;
        LocalDate today = LocalDate.now();
        LocalDateTime todayMidnight = LocalDateTime.of(today, midnight);
        return todayMidnight.plusDays(1);
    }

    /**
     * 没有横线yyyyMMddHHmmss
     *
     * @param date
     * @return
     */
    public static String convertToNoHorizontalLineString(Date date) {
        SimpleDateFormat format = new SimpleDateFormat(HORIZONTAL_LINE_FORMATTER);
        return format.format(date);
    }

    public static String convertToStr(Date date) {
        SimpleDateFormat format = new SimpleDateFormat(FORMATTER);
        return format.format(date);
    }

    public static String convertToSimpleStr(Date date) {
        SimpleDateFormat format = new SimpleDateFormat(SIMPLE_FORMATTER);
        return format.format(date);
    }

    public static String getCurrentFormat() {
        Date current = new Date();
        SimpleDateFormat format = new SimpleDateFormat(YYYY_MM_DD);
        return format.format(current);
    }

    /**
     * 获取除x天以前的若干天对应的空Map(包含今天)
     *
     * @param count 天数
     * @param clazz 类型
     * @return java.util.Map<java.lang.String, T>
     */
    public static <T> Map<String, T> getDateMap(Integer count, Class<T> clazz) {
        Map<String, T> map = new HashMap<>(16);
        Calendar cal = new GregorianCalendar();
        for (int i = 0; i < count; i++) {
            if (0 == i) {
                cal.add(Calendar.DATE, 0);
                map.put(convertToSimpleStr(cal.getTime()), null);
            } else {
                cal.add(Calendar.DATE, -1);
                map.put(convertToSimpleStr(cal.getTime()), null);
            }
        }
        return map;
    }

    /**
     * 获取以前的若干月对应的空Map
     *
     * @param count
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> Map<String, T> getMonthMap(Integer count, Class<T> clazz) {
        Map<String, T> map = new HashMap<>(16);
        Calendar cal = new GregorianCalendar();
        for (int i = 0; i < count; i++) {
            if (0 == i) {
                cal.add(Calendar.MONTH, 0);
                SimpleDateFormat format = new SimpleDateFormat(MONTH_FORMATTER);
                String temp = format.format(cal.getTime());
                map.put(temp, null);
            } else {
                cal.add(Calendar.MONTH, -1);
                SimpleDateFormat format = new SimpleDateFormat(MONTH_FORMATTER);
                String temp = format.format(cal.getTime());
                map.put(temp, null);
            }
        }
        return map;
    }

    /**
     * String转DATE(yyyy-MM-dd)
     *
     * @param str
     * @return
     * @throws ParseException
     */
    public static Date getStringToDate(String str) {
        SimpleDateFormat format = new SimpleDateFormat(DateUtil.FORMATTER);
        Date date = null;
        try {
            date = format.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    //获取当天的开始时间
    public static Date getDayBegin() {
        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    //获取当天的结束时间
    public static Date getDayEnd() {
        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        return cal.getTime();
    }

    //获取昨天的开始时间
    public static Date getBeginDayOfYesterday() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayBegin());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        return cal.getTime();
    }

    //获取昨天的结束时间
    public static Date getEndDayOfYesterDay() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayEnd());
        cal.add(Calendar.DAY_OF_MONTH, -1);
        return cal.getTime();
    }

    //获取明天的开始时间
    public static Date getBeginDayOfTomorrow() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayBegin());
        cal.add(Calendar.DAY_OF_MONTH, 1);

        return cal.getTime();
    }

    //获取明天的结束时间
    public static Date getEndDayOfTomorrow() {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayEnd());
        cal.add(Calendar.DAY_OF_MONTH, 1);
        return cal.getTime();
    }

    /**
     * 获取本周的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfWeek() {
        Date date = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
        if (dayOfWeek == 1) {
            dayOfWeek += 7;
        }
        cal.add(Calendar.DATE, 2 - dayOfWeek);
        return getDayStartTime(cal.getTime());
    }

    /**
     * 获取本周的结束时间
     *
     * @return
     */
    public static Date getEndDayOfWeek() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getBeginDayOfWeek());
        cal.add(Calendar.DAY_OF_WEEK, 6);
        Date weekEndSta = cal.getTime();
        return getDayEndTime(weekEndSta);
    }

    //获取本月的开始时间
    public static Date getBeginDayOfMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getNowYear(), getNowMonth() - 1, 1);
        return getDayStartTime(calendar.getTime());
    }

    public static Date getThreeMonthBegin() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getNowYear(), getNowMonth() - 3, 1);
        return getDayStartTime(calendar.getTime());
    }

    //获取本月的结束时间
    public static Date getEndDayOfMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(getNowYear(), getNowMonth() - 1, 1);
        int day = calendar.getActualMaximum(Calendar.DATE);
        calendar.set(getNowYear(), getNowMonth() - 1, day);
        return getDayEndTime(calendar.getTime());
    }

    /**
     * 获取本年的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfYear() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, getNowYear());
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DATE, 1);
        return getDayStartTime(cal.getTime());
    }

    /**
     * 根据输入的年份获取那一年的开始时间
     *
     * @return
     */
    public static Date getBeginDayOfYearByEnter(Integer year) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, Calendar.JANUARY);
        cal.set(Calendar.DATE, 1);
        return getDayStartTime(cal.getTime());
    }

    /**
     * 获取本年的结束时间
     *
     * @return
     */
    public static Date getEndDayOfYear() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, getNowYear());
        cal.set(Calendar.MONTH, Calendar.DECEMBER);
        cal.set(Calendar.DATE, 31);
        return getDayEndTime(cal.getTime());
    }

    /**
     * 根据输入获取那一年的结束时间
     *
     * @param year
     * @return
     */
    public static Date getEndDayOfYearByEnter(Integer year) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, Calendar.DECEMBER);
        cal.set(Calendar.DATE, 31);
        return getDayEndTime(cal.getTime());
    }

    //获取某个日期的开始时间
    public static Timestamp getDayStartTime(Date d) {
        Calendar calendar = Calendar.getInstance();
        if (null != d) calendar.setTime(d);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return new Timestamp(calendar.getTimeInMillis());
    }

    /**
     * 获取某个日期的结束时间
     *
     * @param d
     * @return
     */
    public static Timestamp getDayEndTime(Date d) {
        Calendar calendar = Calendar.getInstance();
        if (null != d) {
            calendar.setTime(d);
        }
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return new Timestamp(calendar.getTimeInMillis());
    }

    /**
     * 获取今年是哪一年
     *
     * @return
     */
    public static Integer getNowYear() {
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return gc.get(Calendar.YEAR);
    }

    /**
     * 获取输入月后的年份
     *
     * @param month
     * @return
     */
    public static Integer getYearAddMonth(int month) {
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, month);
        return calendar.get(Calendar.YEAR);
    }

    /**
     * 获取本月是哪一月
     *
     * @return
     */
    public static Integer getNowMonth() {
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return gc.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取当前年+月份的字符串 eg.201908
     *
     * @return
     */
    public static String getNowYearAndMonth() {
        String year = getNowYear().toString();
        Integer month = getNowMonth();
        return month < 10 ? year + "0" + month.toString() : year + month.toString();
    }

    /**
     * 获取当前年+月+日的字符串 eg.20190828152325
     *
     * @return
     */
    public static String getNowYearAndMonthAndDay() {
        String result;
        String year = getNowYear().toString();
        Integer month = getNowMonth();
        result = month < 10 ? year + "0" + month.toString() : year + month.toString();
        Integer day = getNowDay();
        result = day < 10 ? result + "0" + day.toString() : result + day.toString();
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        int hour = gc.get(Calendar.HOUR_OF_DAY);
        int minute = gc.get(Calendar.MINUTE);
        int second = gc.get(Calendar.SECOND);
        return result + hour + minute + second;
    }

    /**
     * 获取当前年+月+日的字符串 eg.20190828
     *
     * @return
     */
    public static String getYearAndMonthAndDay() {
        String result;
        String year = getNowYear().toString();
        Integer month = getNowMonth();
        result = month < 10 ? year + "0" + month.toString() : year + month.toString();
        Integer day = getNowDay();
        result = day < 10 ? result + "0" + day.toString() : result + day.toString();
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return result;
    }

    /**
     * 获取增加后的指定月份
     *
     * @param month
     * @return
     */
    public static Integer getAddMonth(int month) {
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, month);
        return calendar.get(Calendar.MONTH) + 1;
    }

    public static Date getAddMonthDate(Date date, int month) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, month);
        return calendar.getTime();
    }

    /**
     * 获取今天是哪一天
     *
     * @return
     */
    public static Integer getNowDay() {
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return gc.get(Calendar.DATE);
    }

    public static Integer getNowHour() {
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return gc.get(Calendar.HOUR_OF_DAY);
    }

    public static Integer getNowMinute() {
        Date date = new Date();
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        return gc.get(Calendar.MINUTE);
    }

    //获取增加月份的日期信息
    public static Integer getaddMonthDay(int month) {
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, month);
        return calendar.get(Calendar.DAY_OF_MONTH);
    }


    //两个日期相减得到的天数
    public static int getDiffDays(Date beginDate, Date endDate) {
        if (beginDate == null || endDate == null) {
            throw new IllegalArgumentException("getDiffDays param is null!");
        }
        long diff = (endDate.getTime() - beginDate.getTime()) / (1000 * 60 * 60 * 24);
        return new Long(diff).intValue();
    }

    //两个日期相减得到的毫秒数
    public static long dateDiff(Date beginDate, Date endDate) {
        long date1ms = beginDate.getTime();
        long date2ms = endDate.getTime();
        return date2ms - date1ms;
    }

    //获取两个日期中的最大日期
    public static Date max(Date beginDate, Date endDate) {
        if (beginDate == null) {
            return endDate;
        }
        if (endDate == null) {
            return beginDate;
        }
        if (beginDate.after(endDate)) {
            return beginDate;
        }
        return endDate;
    }

    //获取两个日期中的最小日期
    public static Date min(Date beginDate, Date endDate) {
        if (beginDate == null) {
            return endDate;
        }
        if (endDate == null) {
            return beginDate;
        }
        if (beginDate.after(endDate)) {
            return endDate;
        }
        return beginDate;
    }

    //返回某月该季度的第一个月
    public static Date getFirstSeasonDate(Date date) {
        final int[] season = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int sean = season[cal.get(Calendar.MONTH)];
        cal.set(Calendar.MONTH, sean * 3 - 3);
        return cal.getTime();
    }

    //返回某个日期下几天的日期
    public static Date getNextDay(Date date, int i) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.DATE, cal.get(Calendar.DATE) + i);
        return cal.getTime();
    }

    //返回某个日期前几天的日期
    public static Date getFrontDay(Date date, int i) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.DATE, cal.get(Calendar.DATE) - i);
        return cal.getTime();
    }

    //获取某年某月到某年某月按天的切片日期集合（间隔天数的集合）
    public static List getTimeList(int beginYear, int beginMonth, int endYear,
                                   int endMonth, int k) {
        List list = new ArrayList();
        if (beginYear == endYear) {
            for (int j = beginMonth; j <= endMonth; j++) {
                list.add(getTimeList(beginYear, j, k));
            }
        } else {
            {
                for (int j = beginMonth; j < 12; j++) {
                    list.add(getTimeList(beginYear, j, k));
                }

                for (int i = beginYear + 1; i < endYear; i++) {
                    for (int j = 0; j < 12; j++) {
                        list.add(getTimeList(i, j, k));
                    }
                }
                for (int j = 0; j <= endMonth; j++) {
                    list.add(getTimeList(endYear, j, k));
                }
            }
        }
        return list;
    }

    //获取某年某月按天切片日期集合（某个月间隔多少天的日期集合）
    public static List getTimeList(int beginYear, int beginMonth, int k) {
        List list = new ArrayList();
        Calendar begincal = new GregorianCalendar(beginYear, beginMonth, 1);
        int max = begincal.getActualMaximum(Calendar.DATE);
        for (int i = 1; i < max; i = i + k) {
            list.add(begincal.getTime());
            begincal.add(Calendar.DATE, k);
        }
        begincal = new GregorianCalendar(beginYear, beginMonth, max);
        list.add(begincal.getTime());
        return list;
    }

}
